package com.zxy.ziems.server.classify.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zxy.btp.common.exception.ConditionException;
import com.zxy.btp.common.response.PageResult;
import com.zxy.btp.common.util.IdWorker;
import com.zxy.ziems.server.classify.convertor.ClassifyConvert;
import com.zxy.ziems.server.classify.mapper.ClassifyMapper;
import com.zxy.ziems.server.classify.pojo.dto.ClassifyDevicePropertyDTO;
import com.zxy.ziems.server.classify.pojo.dto.ClassifyDto;
import com.zxy.ziems.server.classify.pojo.entity.Classify;
import com.zxy.ziems.server.classify.pojo.entity.ClassifyDeviceProperty;
import com.zxy.ziems.server.classify.pojo.param.ClassifyDevicePropertyPageParam;
import com.zxy.ziems.server.classify.pojo.vo.ClassifyDevicePropertyVO;
import com.zxy.ziems.server.classify.pojo.vo.ClassifyTreeNode;
import com.zxy.ziems.server.classify.pojo.vo.ClassifyVO;
import com.zxy.ziems.server.classify.service.ClassifyDevicePropertyService;
import com.zxy.ziems.server.classify.service.ClassifyService;
import com.zxy.ziems.server.constant.ClassifyCountType;
import com.zxy.ziems.server.device.pojo.entity.DataTypeEntity;
import com.zxy.ziems.server.device.pojo.entity.DataUnitEntity;
import com.zxy.ziems.server.device.pojo.entity.DeviceEntity;
import com.zxy.ziems.server.device.service.DataTypeService;
import com.zxy.ziems.server.device.service.DataUnitService;
import com.zxy.ziems.server.device.service.DeviceService;
import com.zxy.ziems.server.domain.ClassifyPointRel;
import com.zxy.ziems.server.domain.DictItem;
import com.zxy.ziems.server.domain.Org;
import com.zxy.ziems.server.scene.pojo.entity.SceneEntity;
import com.zxy.ziems.server.scene.service.SceneService;
import com.zxy.ziems.server.service.ClassifyPointRelService;
import com.zxy.ziems.server.service.DictItemService;
import com.zxy.ziems.server.service.OrgService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.tree.TreeUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName ClassifyServiceImpl
 * @Author 邓伟
 * @Date 2023/11/27 13:40
 * @Version 1.0
 */
@Service
public class ClassifyServiceImpl extends ServiceImpl<ClassifyMapper, Classify> implements ClassifyService {

    @Autowired
    private ClassifyMapper classifyMapper;
    @Autowired
    private ClassifyPointRelService classifyPointRelService;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private ClassifyDevicePropertyService classifyDevicePropertyService;
    @Autowired
    private DeviceService deviceService;

    @Override
    public void addOrUpdate(ClassifyDto classifyDto) {
        String classifyId = classifyDto.getClassifyId();
        checkClassifyName(classifyDto);
        Classify classify = ClassifyConvert.INSTANCE.classifyDtoToClassify(classifyDto);
        //判断上级分类计算方式是否为下级点位合计
        if (StrUtil.isNotEmpty(classifyDto.getParentClassifyId())) {
            checkParentClassify(classifyDto);
        }
        if (StrUtil.isEmpty(classifyId)) {
            if (StrUtil.isEmpty(classifyDto.getParentClassifyId())) {
                classifyId = idWorker.nextId() + "";
                classify.setClassifyId(classifyId);
                classify.setRootClassifyId(classifyId);
            }
            classifyMapper.insert(classify);
        } else {
            classifyMapper.updateById(classify);
        }
    }

    @Override
    public void delete(String classifyId) {
        Classify classify = classifyMapper.selectById(classifyId);
        Integer countType = classify.getCountType();
        if (ClassifyCountType.POINT_COUNT.equals(countType)) {
            //判断是否有绑定点位
            QueryWrapper<ClassifyPointRel> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(ClassifyPointRel::getClassifyId, classifyId);
            long countPoint = classifyPointRelService.count(queryWrapper);
            if (countPoint > 0) {
                throw new ConditionException("该分类下存在绑定的点位，不允许删除");
            }
        } else {
            //判断是否有下级分类
            QueryWrapper<Classify> query = new QueryWrapper<>();
            query.lambda().eq(Classify::getParentClassifyId, classifyId);
            Long count = classifyMapper.selectCount(query);
            if (count > 0) {
                throw new ConditionException("该分类下有下级分类，不允许删除");
            }
        }
        classifyMapper.deleteById(classifyId);
    }

    @Override
    public List<ClassifyTreeNode> getTree(Integer dataObtainType, Integer type) {
        QueryWrapper<Classify> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ObjectUtil.isNotEmpty(dataObtainType), Classify::getDataObtainType, dataObtainType);
        queryWrapper.lambda().orderByAsc(Classify::getSort);
        List<Classify> classifyList = list(queryWrapper);
        if (CollectionUtil.isEmpty(classifyList)) {
            return new ArrayList<>();
        }

        List<ClassifyTreeNode> list = new ArrayList<>(classifyList.stream().map(classify -> new ClassifyTreeNode(
                classify.getClassifyId(),
                classify.getClassifyName(),
                classify.getParentClassifyId(),
                classify.getSort(),
                classify.getCountType(),
                classify.getRootClassifyId(),
                classify.getDataObtainType(),
                classify.getDescription(),
                0)
        ).toList());

        if (Objects.equals(1, type)) {
            // 获取根据设备分组的信息
            Map<String, List<DeviceEntity>> deviceMap = getGroupByClassifyId();
            for (String classifyId : deviceMap.keySet()) {
                List<DeviceEntity> deviceEntityList = deviceMap.get(classifyId);
                deviceEntityList.forEach(m -> {
                    ClassifyTreeNode node = new ClassifyTreeNode(classifyId + "-" + m.getId(), m.getName(), classifyId
                            , 999, null, classifyId, null, null, 1);
                    list.add(node);
                });
            }
        }

        return TreeUtils.tree(list);
    }


    public Map<String, List<DeviceEntity>> getGroupByClassifyId() {
        List<ClassifyDeviceProperty> classifyDevicePropertyList = classifyDevicePropertyService.list();
        Map<String, List<ClassifyDeviceProperty>> map = classifyDevicePropertyList.stream().collect(Collectors.groupingBy(m -> m.getClassifyId()));

        List<DeviceEntity> deviceEntityList = deviceService.list();
        Map<String, List<DeviceEntity>> mapResult = new HashMap<>();
        for (String s : map.keySet()) {
            List<ClassifyDeviceProperty> classifyDevicePropertyList1 = map.get(s);
            List<String> deviceIds = classifyDevicePropertyList1.stream().map(m -> m.getDeviceId()).collect(Collectors.toList());
            List<DeviceEntity> deviceEntityOneList = deviceEntityList.stream().filter(m -> deviceIds.contains(m.getId())).collect(Collectors.toList());
            mapResult.put(s, deviceEntityOneList);
        }


        return mapResult;
    }

    @Override
    public List<String> getClassifyId(String parentClassifyId) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().eq(Classify::getParentClassifyId, parentClassifyId);
        List<Classify> classifyList = classifyMapper.selectList(query);
        if (CollectionUtil.isEmpty(classifyList)) {
            return List.of(parentClassifyId);
        } else {
            List<String> classifyIdList = classifyList.stream().map(Classify::getClassifyId).collect(Collectors.toList());
            List<String> idList = getClassifyId(classifyIdList);
            idList.add(parentClassifyId);
            return idList;
        }
    }

    @Override
    public List<String> getByIdList(List<String> queryIdList) {
        List<String> idList = new ArrayList<>();
        for (String classifyId : queryIdList) {
            List<String> list = getClassifyId(classifyId);
            if (CollectionUtil.isNotEmpty(list)) {
                idList.addAll(list);
            }
        }
        return idList;
    }

    @Override
    public List<Classify> getChildrenClassify(String classifyId) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().eq(Classify::getParentClassifyId, classifyId);
        return classifyMapper.selectList(query);
    }

    @Override
    public List<String> getChildrenId(String classifyId) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().eq(Classify::getParentClassifyId, classifyId)
                .eq(Classify::getDeleteStatus, false).orderByAsc(Classify::getSort);
        return classifyMapper.selectList(query).stream().map(m -> m.getClassifyId()).collect(Collectors.toList());
    }

    private List<String> getClassifyId(List<String> classifyIdList) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().in(Classify::getParentClassifyId, classifyIdList);
        List<Classify> classifyList = classifyMapper.selectList(query);
        if (CollectionUtil.isEmpty(classifyList)) {
            return classifyIdList;
        } else {
            List<String> idList = classifyList.stream().map(Classify::getClassifyId).collect(Collectors.toList());
            List<String> returnList = getClassifyId(idList);
            returnList.addAll(classifyIdList);
            return returnList;
        }
    }

    private void checkParentClassify(ClassifyDto classifyDto) {
        Classify parentClassify = classifyMapper.selectById(classifyDto.getParentClassifyId());
        AssertUtil.nonNull(parentClassify, "父节点不存在");
        Integer countType = parentClassify.getCountType();
        if (ClassifyCountType.POINT_COUNT.equals(countType)) {
            throw new ConditionException("计算方式为下级点位合计时，不允许添加下级分类");
        }
    }

    private void checkClassifyName(ClassifyDto classifyDto) {
        String classifyId = classifyDto.getClassifyId();
        String classifyName = classifyDto.getClassifyName();
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().eq(Classify::getClassifyName, classifyName)
                .eq(Classify::getParentClassifyId, classifyDto.getParentClassifyId());
        if (StrUtil.isNotEmpty(classifyId)) {
            query.lambda().ne(Classify::getClassifyId, classifyId);
        }
        Long count = classifyMapper.selectCount(query);
        if (count > 0) {
            throw new ConditionException("分类名称重复，请修改后再试");
        }

    }

    @Override
    public List<ClassifyTreeNode> deviceListByClassId(String classifyId) {
        List<ClassifyTreeNode> res = new ArrayList<>();
        List<ClassifyDeviceProperty> list = classifyDevicePropertyService.getByClassId(classifyId);
        if (CollectionUtil.isEmpty(list)) {
            return res;
        }
        List<DeviceEntity> devices = deviceService.listByIdList(list.stream().map(ClassifyDeviceProperty::getDeviceId).collect(Collectors.toList()));
        devices.stream().collect(Collectors.toMap(DeviceEntity::getId, DeviceEntity::getName))
                .forEach((k, v) -> {
                    ClassifyTreeNode node = new ClassifyTreeNode(k, v, classifyId, 1);
                    res.add(node);
                });
        return res;
    }

    /**
     * 根据分类id，查询该分类所有下级分类信息
     *
     * @param classifyId
     * @return
     */
    @Override
    public List<Classify> classifyListByClassifyId(String classifyId) {
        List<Classify> classifyList = new ArrayList<>();

        // 查询分类id信息
        QueryWrapper<Classify> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Classify::getClassifyId, classifyId);
        Classify classify = this.getOne(queryWrapper);
        classifyList.add(classify);

        List<String> ids = new ArrayList<>();
        ids.add(classifyId);

        // 循环查询所有子分类
        while (!ids.isEmpty()) {
            QueryWrapper<Classify> sonQueryWrapper = new QueryWrapper<>();
            sonQueryWrapper.lambda().in(Classify::getParentClassifyId, ids);
            List<Classify> classifySonList = this.list(sonQueryWrapper);

            ids = classifySonList.stream().map(Classify::getClassifyId).collect(Collectors.toList());
            classifyList.addAll(classifySonList);
        }


        return classifyList;
    }

    @Override
    public ClassifyVO detailById(String id) {
        Classify entity = getById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        DataUnitEntity unit = dataUnitService.getById(entity.getUnitId());
        return ClassifyConvert.INSTANCE.classifyDO2ClassifyVO(entity, unit);
    }

    @Override
    public Classify detailByDescription(String description) {
        LambdaQueryWrapper<Classify> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Classify::getDescription, description);
        Classify classify = classifyMapper.selectOne(wrapper);
        return classify;
    }

    @Override
    public List<Classify> classifysByDescription(String description) {
        LambdaQueryWrapper<Classify> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(Classify::getDescription, description);
        List<Classify> classifies = classifyMapper.selectList(wrapper);
        return classifies;
    }

    @Override
    public PageResult<ClassifyDevicePropertyVO> pageListBy(ClassifyDevicePropertyPageParam param) {
        PageResult<ClassifyDeviceProperty> result = classifyDevicePropertyService.pageListBy(param);


        List<ClassifyDeviceProperty> list = result.getList();
        Set<String> deviceIdSet = list.stream().map(ClassifyDeviceProperty::getDeviceId).collect(Collectors.toSet());

        if (CollectionUtil.isEmpty(deviceIdSet)) return new PageResult<>(0L, new ArrayList<>());

        Set<String> classifyIdSet = list.stream().map(ClassifyDeviceProperty::getClassifyId).collect(Collectors.toSet());
        List<Classify> classifies = this.listByIds(classifyIdSet);
        Map<String, Classify> classifyMap = classifies.stream().collect(Collectors.toMap(Classify::getClassifyId, (a) -> a));

        List<DeviceEntity> deviceEntities = deviceService.listByIdList(deviceIdSet);
        Map<String, DeviceEntity> deviceEntityMap = deviceEntities.stream().collect(Collectors.toMap(DeviceEntity::getId, (a) -> a));
        Map<String, SceneEntity> sceneMap = sceneService.list().stream().collect(Collectors.toMap(SceneEntity::getId, (a) -> a));
        Set<String> orgIdSet = deviceEntities.stream().map(DeviceEntity::getOrgId).collect(Collectors.toSet());
        List<Org> orgList = orgService.listByIds(orgIdSet);
        Map<String, Org> orgMap = orgList.stream().collect(Collectors.toMap(Org::getOrgId, (a) -> a));

        ClassifyConvert instance = ClassifyConvert.INSTANCE;
        List<ClassifyDevicePropertyVO> resultList = list.stream().map((a) -> {
            DeviceEntity deviceEntity = deviceEntityMap.get(a.getDeviceId());
            Classify classify = classifyMap.get(a.getClassifyId());

            SceneEntity floor, building;
            Org org;
            if (Objects.isNull(deviceEntity)) {
                floor = null;
                building = null;
                org = null;
                classify = null;
            } else {
                floor = StrUtil.isBlank(deviceEntity.getSceneId()) ? null : sceneMap.get(deviceEntity.getSceneId());
                building = Objects.isNull(floor) ? null : sceneMap.get(floor.getParentId());
                org = StrUtil.isBlank(deviceEntity.getOrgId()) ? null : orgMap.get(deviceEntity.getOrgId());
            }
            DataTypeEntity dataType = dataTypeService.getById(a.getDataTypeId());

            return instance.classifyDevicePropertyDO2VO(a, classify, deviceEntity, building, floor, org, dataType);

        }).collect(Collectors.toList());
        return new PageResult<>(result.getTotal(), resultList);
    }

    @Override
    public void saveClassifyDevice(ClassifyDevicePropertyDTO dto) {
        DeviceEntity deviceEntity = deviceService.getById(dto.getDeviceId());
        AssertUtil.nonNull(deviceEntity, "设备不存在");

        ClassifyDeviceProperty entity = ClassifyConvert.INSTANCE.classifyDevicePropertyDto2Do(dto, deviceEntity);
        classifyDevicePropertyService.save(entity);
    }

    @Override
    public List<ClassifyVO> getByParentClassifyId(String parentClassifyId) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().eq(Classify::getParentClassifyId, parentClassifyId);
        List<Classify> classifies = classifyMapper.selectList(query);
        return classifies.stream().map(m -> ClassifyConvert.INSTANCE.classifyDO2ClassifyVO(m, null)).collect(Collectors.toList());
    }

    private DataUnitService dataUnitService;

    @Autowired
    public void setDataUnitService(DataUnitService dataUnitService) {
        this.dataUnitService = dataUnitService;
    }


    private SceneService sceneService;

    @Autowired
    public void setSceneService(SceneService sceneService) {
        this.sceneService = sceneService;
    }

    private OrgService orgService;

    @Autowired
    public void setOrgService(OrgService orgService) {
        this.orgService = orgService;
    }


    private DataTypeService dataTypeService;

    @Autowired
    public void setDataTypeService(DataTypeService dataTypeService) {
        this.dataTypeService = dataTypeService;
    }

    @Autowired
    private DictItemService dictItemService;

    /**
     * 根据字典获取
     *
     * @param dictCode
     * @param itemName
     * @return
     */
    @Override
    public List<ClassifyVO> getByParentByDict(String dictCode, String itemName) {
        String classifyId = getclassifyId(dictCode, itemName);

        return getByParentClassifyId(classifyId);
    }

    @Override
    public List<ClassifyVO> getByParentClassifyIds(List<String> parentClassifyIds) {
        QueryWrapper<Classify> query = new QueryWrapper<>();
        query.lambda().in(Classify::getParentClassifyId, parentClassifyIds);
        List<Classify> classifies = classifyMapper.selectList(query);
        return classifies.stream().map(m -> ClassifyConvert.INSTANCE.classifyDO2ClassifyVO(m, null)).collect(Collectors.toList());
    }

    @Override
    public List<ClassifyTreeNode> treeByParentClassifyId(Integer dataObtainType, Integer type, String parentClassifyId) {
        AssertUtil.isTrue(StringUtils.isNotBlank(parentClassifyId), "parentClassifyId不可为空");
        List<ClassifyTreeNode> treeNodeList = this.getTree(dataObtainType, type);
        ClassifyTreeNode parentData = getParentData(treeNodeList, parentClassifyId);
        return Arrays.asList(parentData);
    }

    private ClassifyTreeNode getParentData(List<ClassifyTreeNode> treeNodeList, String parentClassifyId) {
        for (ClassifyTreeNode classifyTreeNode : treeNodeList) {
            if (classifyTreeNode.getClassifyId().equals(parentClassifyId)) {
                return classifyTreeNode;
            }
            if (!CollectionUtils.isEmpty(classifyTreeNode.getChildren())) {
                ClassifyTreeNode parentData = getParentData(classifyTreeNode.getChildren(), parentClassifyId);
                if (parentData != null) {
                    return parentData;
                }
            }
        }
        return null;
    }

    /**
     * 根据item获取classifyId
     *
     * @param dictCode
     * @param itemName
     * @return
     */
    @Nullable
    private String getclassifyId(String dictCode, String itemName) {
        String classifyId = null;
        // 获取字典配置分类项
        List<DictItem> dictItems = dictItemService.getByDictCode(dictCode);
        if (dictItems.isEmpty()) {
            classifyId = null;
        } else {
            DictItem dictItem = dictItems.stream().filter(m -> m.getItemName().equals(itemName)).findFirst().orElse(new DictItem());
            classifyId = dictItem.getItemCode();
        }
        AssertUtil.nonNull(classifyId, "未找到字典配置");
        return classifyId;
    }
}
